package com.druid.service;

import cn.hutool.core.util.StrUtil;
import com.druid.common.config.OpenAIConfig;
import com.druid.enums.OpenAiApiTypeEnum;
import com.druid.model.ImageOptions;
import com.druid.request.PicAiRequest;
import com.druid.request.openai.OpenAIApiRequest;
import com.druid.request.openai.chat.OpenAIChatRequest;
import com.druid.request.openai.chat.OpenAIRequest;
import com.druid.request.openai.chat.OpenAIRequestBaseAbstract;
import com.druid.request.openai.image.ImageCreateRequest;
import com.druid.request.openai.image.ImageCreateVariationRequest;
import com.druid.request.openai.image.ImageEditRequest;
import com.druid.request.openai.image.ImageRequestBaseAbstract;
import com.druid.response.openai.ImageResponse;
import com.druid.response.openai.OpenAIChatResponse;
import com.druid.response.openai.OpenAIResponse;
import com.druid.util.ManagementException;
import com.druid.util.UtilForImage;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.awt.*;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@SuppressWarnings("unused")
@Log4j2
@Component
@AllArgsConstructor
public class OpenAIService {

    private final RestTemplate restTemplate;

    private final OpenAIConfig openAIConfig;

    /**
     * 访问openAi 的api获取询问信息
     * @param openAIRequest     openAi 参数设置请求体
     * @return                  openAi 返回的响应参数
     */
    public <T> T generateByJsonString (OpenAIApiRequest openAIRequest, Class<T> tClass, String openAiMainUrl, String apikey ){

        //  设置请求投
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", "Bearer " + apikey);

        //  转化实体为json字符串
        ObjectMapper objectMapper = new ObjectMapper();
        String requestBody;
        try {
            requestBody = objectMapper.writeValueAsString(openAIRequest);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("josn字符串转化异常");
        }
        HttpEntity<String> request = new HttpEntity<>(requestBody, headers);

        //  发起请求，调用openai api接口，并将获取的信息返回
        return restTemplate.postForObject(openAiMainUrl, request, tClass);

    }

    public <T> T generateByMultipart(OpenAIApiRequest openAIRequest, Class<T> tClass, String openAiMainUrl, String apikey) throws IOException {

        // 创建请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        headers.setBearerAuth(apikey);

        // 封装请求体
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();

        //  遍历父类字段
        Field[] declaredFields = openAIRequest.getClass().getSuperclass().getDeclaredFields();
        List<Field> parentFields = Arrays.stream(declaredFields).collect(Collectors.toList());

        // 遍历 OpenAIApiRequest 对象的属性
        Field[] fields = openAIRequest.getClass().getDeclaredFields();
        List<Field> childFields = Arrays.stream(fields).collect(Collectors.toList());
        childFields.addAll(parentFields);
        for (Field field : childFields) {
            // 获取属性名和属性值
            String name = field.getName();
            Object value;
            try {
                field.setAccessible(true);
                value = field.get(openAIRequest);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
            // 如果属性值为空，则不添加到请求体中
            if (value == null) {
                continue;
            }
            // 如果属性值是 ByteArrayOutputStream 类型，则将其转换成 ByteArrayResource
            if (OutputStream.class.isAssignableFrom(field.getType())) {
                ByteArrayOutputStream byteArrayOutputStream = (ByteArrayOutputStream)value;
                ByteArrayResource resource = new ByteArrayResource(byteArrayOutputStream.toByteArray()) {
                    @Override
                    public String getFilename() {
                        return "image.png";
                    }
                };
                body.add(name, resource);
            } else if (InputStream.class.isAssignableFrom(field.getType())){
                InputStream inputStream = (InputStream) value;
                // 使用 Java 8 将 InputStream 转换为 byte[]
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    byteArrayOutputStream.write(buffer, 0, bytesRead);
                }
                byte[] imageBytes = byteArrayOutputStream.toByteArray();
                // 添加图片文件
                body.add(name, new ByteArrayResource(imageBytes) {
                    @Override
                    public String getFilename() {
                        return "image.png"; // 这里可以设置文件名
                    }
                });
            }else {
                body.add(name, value.toString());
            }
        }

        // 创建 HttpEntity 对象并发送 POST 请求
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
        return restTemplate.postForObject(openAiMainUrl, requestEntity, tClass);
    }


    /**
     * 访问openAi 的api获取询问信息
     * @param openAIRequest     openAi 参数设置请求体
     * @return                  openAi 给出的询问答案
     */
    public String generateText( OpenAIRequestBaseAbstract openAIRequest, OpenAiApiTypeEnum openAiApiTypeEnum ) {

        switch (openAiApiTypeEnum) {

            case CREATE_COMPLETION:
                OpenAIResponse response = generateByJsonString(openAIRequest,OpenAIResponse.class,openAiApiTypeEnum.getUrl(),openAIConfig.getApiKey());
                return response == null ? null :
                        response.getChoices() == null || response.getChoices().length == 0 ? null :
                                response.getChoices()[0].getText();

            case CREATE_CHAT_COMPLETION:
                OpenAIChatResponse responseChat = generateByJsonString(openAIRequest, OpenAIChatResponse.class, openAiApiTypeEnum.getUrl(),openAIConfig.getApiKey());
                return responseChat == null ? null :
                        responseChat.getChoices() == null || responseChat.getChoices().length == 0 ? null :
                                responseChat.getChoices()[0].getMessage() == null ? null :
                                responseChat.getChoices()[0].getMessage().getContent();
        }

        throw new RuntimeException("传入的枚举类型没有匹配到");
    }


    public String generateText( String prompt, String userId ){

        OpenAiApiTypeEnum createCompletion = OpenAiApiTypeEnum.CREATE_COMPLETION;
        OpenAIRequest openAIRequest = new OpenAIRequest(createCompletion.getUseModel(), prompt, openAIConfig.getMaxTokens(), openAIConfig.getTemperature(), openAIConfig.getStop(), userId);
        return generateText(openAIRequest,createCompletion);
    }

    public String generateChatText( String[] content, String userId ){

        OpenAiApiTypeEnum createChatCompletion = OpenAiApiTypeEnum.CREATE_CHAT_COMPLETION;

        OpenAIChatRequest.Messages[] messagesArray = Arrays.stream(content).map(s -> {
            OpenAIChatRequest.Messages messages = new OpenAIChatRequest.Messages();
            messages.setContent(s);
            messages.setRole("user");
            return messages;
        }).toArray(OpenAIChatRequest.Messages[]::new);

        OpenAIChatRequest openAIChatRequest = new OpenAIChatRequest(createChatCompletion.getUseModel(), messagesArray, openAIConfig.getMaxTokens(), openAIConfig.getTemperature(), openAIConfig.getStop(), userId);
        return generateText(openAIChatRequest,createChatCompletion);
    }

    /**
     * ai图片生成
     */
    public ImageResponse generateImage( PicAiRequest picAiRequest ) throws IOException {

        ImageRequestBaseAbstract imageRequestBaseAbstract;
        String openAiMainUrl;
        PicAiRequest.picApiType picApiType = picAiRequest.getPicApiType();

        switch (picApiType){

            case CRATE_IMAGE:
                imageRequestBaseAbstract = new ImageCreateRequest(picAiRequest.getPrompt());
                openAiMainUrl = OpenAiApiTypeEnum.CRATE_IMAGE.getUrl();
                if ( picAiRequest.getPicCount() > 1 ) imageRequestBaseAbstract.setN(picAiRequest.getPicCount());
                if ( StrUtil.isNotBlank(picAiRequest.getSize()) ) imageRequestBaseAbstract.setSize(picAiRequest.getSize());
                return generateByJsonString(imageRequestBaseAbstract,ImageResponse.class,openAiMainUrl,openAIConfig.getApiKey());

            case CREATE_IMAGE_VARIATION:
                imageRequestBaseAbstract = new ImageCreateVariationRequest(picAiRequest.getImage().getInputStream());
                openAiMainUrl = OpenAiApiTypeEnum.CREATE_IMAGE_VARIATION.getUrl();
                if ( picAiRequest.getPicCount() > 1 ) imageRequestBaseAbstract.setN(picAiRequest.getPicCount());
                if ( StrUtil.isNotBlank(picAiRequest.getSize()) ) imageRequestBaseAbstract.setSize(picAiRequest.getSize());
                return generateByMultipart(imageRequestBaseAbstract,ImageResponse.class,openAiMainUrl,openAIConfig.getApiKey());

            case CRATE_IMAGE_EDIT:
                openAiMainUrl = OpenAiApiTypeEnum.CRATE_IMAGE_EDIT.getUrl();
                PicAiRequest.Rectangle rectangleObject = picAiRequest.getRectangleObject();
                Rectangle rectangle = new Rectangle((int) rectangleObject.getStartX(), (int) rectangleObject.getStartY(), (int) rectangleObject.getWidth(), (int) rectangleObject.getHeight());
                if ("1".equals(picAiRequest.getPicModel())) {
                    ImageOptions imageOptions = new ImageOptions(picAiRequest.getImage().getInputStream(),rectangle,true);
                    UtilForImage.transparentImage(imageOptions);
                    imageRequestBaseAbstract = new ImageEditRequest(imageOptions.getNewImageStream(),imageOptions.getMaskStream(),picAiRequest.getPrompt());
                    if ( picAiRequest.getPicCount() > 1 ) imageRequestBaseAbstract.setN(picAiRequest.getPicCount());
                    if ( StrUtil.isNotBlank(picAiRequest.getSize()) ) imageRequestBaseAbstract.setSize(picAiRequest.getSize());
                    return generateByMultipart(imageRequestBaseAbstract,ImageResponse.class,openAiMainUrl,openAIConfig.getApiKey());
                }

                if ("2".equals(picAiRequest.getPicModel())) {
                    ImageOptions imageOptions = new ImageOptions(picAiRequest.getImage().getInputStream(),rectangle,false);
                    UtilForImage.transparentImage(imageOptions);
                    imageRequestBaseAbstract = new ImageEditRequest(imageOptions.getNewImageStream(),imageOptions.getMaskStream(),picAiRequest.getPrompt());
                    if ( picAiRequest.getPicCount() > 1 ) imageRequestBaseAbstract.setN(picAiRequest.getPicCount());
                    if ( StrUtil.isNotBlank(picAiRequest.getSize()) ) imageRequestBaseAbstract.setSize(picAiRequest.getSize());
                    return generateByMultipart(imageRequestBaseAbstract,ImageResponse.class,openAiMainUrl,openAIConfig.getApiKey());
                }

                throw new ManagementException("图片模式参数不符合要求");

            default:
                throw new ManagementException("未匹配到相关图片API");
        }
    }

    private OutputStream transInputStreamToOutput(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, len);
        }
        return outputStream;
    }

}

